Skip to content

fix: resolve several drag & drop issues #1845

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open

fix: resolve several drag & drop issues #1845

wants to merge 8 commits into from

Conversation

nperez0111
Copy link
Contributor

@nperez0111 nperez0111 commented Jul 11, 2025

This adjusts the logic for drag & drop (especially between editor instances) to handle things better. It is surprisingly complicated to get drag & drop to work nicely across editor instances.

The problem is that each editor instance is listening for events on it's own HTMLElement, but we want to support dragging near an element, and allowing the drop to occur anyway (despite not actually dropping on the element, just near it). To do this, we have to fiddle with the event handling (since we still want for each editor instance to only listen for drag & drop events on it's own element) such that we create synthetic events to trick the editor into doing what the user intended, but did not actually do.

We need to fire these synthetic events under very specific circumstances:

  • When the drag-over or drop event is close to an editor element (within a range, right now 250px), but not within an editor

But, now that we are sometimes triggering these synthetic events, Prosemirror does not have the full-scope of what we've implemented so when dragging & dropping across different editor instances we need to either:

  • When the editor is the drop point, but not the drag point, we need to collapse the selection
    • When the content is dragged outside of editor, but should be dropped into the editor, we need to fire a synthetic event
  • When the editor is the drag point, but not the drop point, we need to delete the selection

This leads to some very complex logic & several edge cases. For example, there are differences between dragging text vs. using the side menu handle.

I also want to fix the drag previews in this PR (This seems to not be possible in firefox)

TODO

  • I want to revisit the purpose of the sideMenuDetection, now that it is not used for the drag & drop of content

Copy link

vercel bot commented Jul 11, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Updated (UTC)
blocknote ✅ Ready (Inspect) Visit Preview Jul 14, 2025 1:59pm
blocknote-website ❌ Failed (Inspect) Jul 14, 2025 1:59pm

Copy link

pkg-pr-new bot commented Jul 11, 2025

Open in StackBlitz

@blocknote/ariakit

npm i https://pkg.pr.new/TypeCellOS/BlockNote/@blocknote/ariakit@1845

@blocknote/code-block

npm i https://pkg.pr.new/TypeCellOS/BlockNote/@blocknote/code-block@1845

@blocknote/core

npm i https://pkg.pr.new/TypeCellOS/BlockNote/@blocknote/core@1845

@blocknote/mantine

npm i https://pkg.pr.new/TypeCellOS/BlockNote/@blocknote/mantine@1845

@blocknote/react

npm i https://pkg.pr.new/TypeCellOS/BlockNote/@blocknote/react@1845

@blocknote/server-util

npm i https://pkg.pr.new/TypeCellOS/BlockNote/@blocknote/server-util@1845

@blocknote/shadcn

npm i https://pkg.pr.new/TypeCellOS/BlockNote/@blocknote/shadcn@1845

@blocknote/xl-ai

npm i https://pkg.pr.new/TypeCellOS/BlockNote/@blocknote/xl-ai@1845

@blocknote/xl-docx-exporter

npm i https://pkg.pr.new/TypeCellOS/BlockNote/@blocknote/xl-docx-exporter@1845

@blocknote/xl-email-exporter

npm i https://pkg.pr.new/TypeCellOS/BlockNote/@blocknote/xl-email-exporter@1845

@blocknote/xl-multi-column

npm i https://pkg.pr.new/TypeCellOS/BlockNote/@blocknote/xl-multi-column@1845

@blocknote/xl-odt-exporter

npm i https://pkg.pr.new/TypeCellOS/BlockNote/@blocknote/xl-odt-exporter@1845

@blocknote/xl-pdf-exporter

npm i https://pkg.pr.new/TypeCellOS/BlockNote/@blocknote/xl-pdf-exporter@1845

commit: cc8d466

Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

A unified, document-level drag & drop implementation to handle cross-editor operations by synthesizing events based on proximity, replacing per-instance handlers and resolving cursor/display issues.

  • Replaced legacy onDrop/onDragEnd in SideMenuPlugin with onDragOver, onDrop, and onDragEnd that use getDragEventContext
  • Added findClosestEditorElement, dispatchSyntheticEvent, and closeDropCursor for synthetic event management within 250px thresholds
  • Bumped prosemirror-dropcursor from 1.8.1 to 1.8.2

Reviewed Changes

Copilot reviewed 2 out of 3 changed files in this pull request and generated 3 comments.

File Description
packages/core/src/extensions/SideMenu/SideMenuPlugin.ts Reworked drag & drop logic: removed old handlers, added proximity detection, context extraction, and synthetic event dispatchers
packages/core/package.json Updated prosemirror-dropcursor dependency to ^1.8.2
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported
Comments suppressed due to low confidence (2)

packages/core/src/extensions/SideMenu/SideMenuPlugin.ts:432

  • The new getDragEventContext logic contains multiple edge cases (origin vs. drop point vs. bounds). Consider adding unit tests to cover each scenario for regression safety.
  getDragEventContext = (event: DragEvent) => {

packages/core/src/extensions/SideMenu/SideMenuPlugin.ts:517

  • The selection collapse now uses selection.anchor, which differs from the original selection.to behavior and may shift the drop insertion point. Consider restoring this.pmView.state.tr.selection.to to match prior logic.
          TextSelection.create(

Comment on lines +617 to 620
const evt = new Event(event.type as "dragover", event) as any;
const editorBoundingBox = this.pmView.dom.getBoundingClientRect();
evt.clientX = event.clientX;
evt.clientY = event.clientY;
Copy link
Preview

Copilot AI Jul 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Creating synthetic drag/drop events with new Event omits the native DragEvent properties. Use new DragEvent(event.type, { ...eventInit }) to ensure dataTransfer, coordinates, and default behavior are preserved.

Suggested change
const evt = new Event(event.type as "dragover", event) as any;
const editorBoundingBox = this.pmView.dom.getBoundingClientRect();
evt.clientX = event.clientX;
evt.clientY = event.clientY;
const editorBoundingBox = this.pmView.dom.getBoundingClientRect();
const evt = new DragEvent(event.type, {
clientX: event.clientX,
clientY: event.clientY,
dataTransfer: event.dataTransfer,
});

Copilot uses AI. Check for mistakes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to refactor this, but there is something finicky with drag events that they drop the data transfer

const closestEditor = this.findClosestEditorElement(event);

// We arbitrarily decide how far is "too far" from the closest editor to be considered a drop point
if (!closestEditor || closestEditor.distance > 250) {
Copy link
Preview

Copilot AI Jul 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The 250px threshold is a magic number. Extract it into a named constant (e.g., MAX_PROXIMITY_PX) to clarify its purpose and simplify future adjustments.

Suggested change
if (!closestEditor || closestEditor.distance > 250) {
if (!closestEditor || closestEditor.distance > MAX_PROXIMITY_PX) {

Copilot uses AI. Check for mistakes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@matthewlipski not sure what the best thing to do here would be. Likely a percentage like you are using elsewhere in the file?

Copy link
Collaborator

@YousefED YousefED left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great!! The comments make things so much clearer :)

Some UX glitches I encountered when dragging in a single editor:

see gif, in order of appearance (I think all of these don't occur on the live version):

  1. dragging to side+below an editor moves drag line to top (instead of at bottom)
  2. dropping a block opens the formatting toolbar
  3. dropping a block to the side doesn't seem to work

dragging

* - If the dragover event is outside the bounds of the current editor, then it will dispatch a synthetic dragleave event to the current editor
* (which will trigger the drop-cursor to be removed from the current editor)
*
* The synthetic event is a necessary evil because we do not control prosemirror-dropcursor to be able to show the drop-cursor within the range we want
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would it be a lot easier if we fork dropcursor?

normally I'd not suggest this, but given we already have an alternative dropcursor plugin for mult-column, it might be a cleaner solution?

@@ -93,7 +93,7 @@
"@tiptap/pm": "^2.12.0",
"emoji-mart": "^5.6.0",
"hast-util-from-dom": "^5.0.1",
"prosemirror-dropcursor": "^1.8.1",
"prosemirror-dropcursor": "^1.8.2",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we'll probably need to update our multi-column dropcursor as well (see comment below)

}

if (
this.sideMenuDetection === "editor" ||
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

did we drop sideMenuDetection? then we also need to remove it from editor options / docs / etc

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

code block cannot be moved (example page) Dragging text around copies the text instead of moving it
2 participants